﻿// SyntheticDataGenerator.Generators.NormalGenerator
//
// (c) 2011 Arthur Pitman
//
// Part of Market-Basket Synthetic Data Generator
// A C# adaptation of the IBM Quest Market-Basket Synthetic Data Generator
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// (Version 2) as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License (Version 2) along with this program; if not, write to 
// the Free Software Foundation, Inc., 
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// or see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SyntheticDataGenerator.Generators
{
	/// <summary>
	/// Generates random numbers with a normal distribution
	/// 
	/// See "Numerical Recipes in C: The Art of Scientific Computing" by Press et al.
	/// </summary>
	public class NormalGenerator : IGenerator
	{        
		/// <summary>
		/// Mean
		/// </summary>
		protected double mu;

		/// <summary>
		/// Variance, i.e. (standard deviation)^2
		/// </summary>
		protected double sigma;	

		/// <summary>
		/// Flag to indicate if an extra deviate has already been created
		/// </summary>
		protected bool hasExtraDeviate = false;
		
		/// <summary>
		/// Value of extra deviate, if available
		/// </summary>
		protected double extraDeviate;

		/// <summary>
		/// The <see cref="UniformGenerator"/> used for randomness
		/// </summary>
		protected UniformGenerator uniformGenerator = new UniformGenerator();

		/// <summary>
		/// Initializes a new <see cref="ExponentialGenerator"/> instance with the specified mean
		/// </summary>
		/// <param name="mu">The mean of the distribution</param>
		/// <param name="sigma">The variance of the distribution</param>
		public NormalGenerator(double mu, double sigma)        
		{
			this.mu = mu;
			this.sigma = sigma;
		}

		/// <summary>
		/// Returns a normally distributed random deviate
		/// </summary>
		/// <returns>The deviate</returns>
		protected double GetDeviate()
		{
			// check if an existing deviate is available
			if (hasExtraDeviate)
			{
				hasExtraDeviate = false;
				return extraDeviate;
			}

			// create two deviates using the box-Muller transformation 
			double rsq, v1, v2;
			do
			{
				// pick two uniform numbers and scale them from -1 to +1
				v1 = 2.0 * uniformGenerator.Next() - 1.0;	
				v2 = 2.0 * uniformGenerator.Next() - 1.0;
				
				rsq = v1 * v1 + v2 * v2;		
			}
			while (rsq >= 1.0 || rsq == 0.0);	// repeat if v1 and v2 are not inside the unit circle

			double fac = Math.Sqrt(-2.0 * Math.Log(rsq) / rsq);

			// save one deviate for next time
			extraDeviate = v1 * fac;
			hasExtraDeviate = true;

			// return other deviate immediately
			return v2 * fac;
		}

		/// <summary>
		/// Returns a random variable with normal distribution by scaling a normally distributed deviate
		/// </summary>
		/// <returns>The random variable</returns>
		public double Next()	
		{
			return GetDeviate() * sigma + mu;
		}

	}
}
